import { injectable } from '@/services/service'
import { Worker, createWorker } from 'tesseract.js'
import { invoke, convertFileSrc } from '@tauri-apps/api/tauri'
import { appCacheDir, documentDir, join } from '@tauri-apps/api/path'
import { createDir, readTextFile, exists, readDir, writeTextFile, BaseDirectory, removeFile } from '@tauri-apps/api/fs'
import { ref } from 'vue'

const identifier = 'tech.jiyun.ascum'
const ROIS_1080 = [
	[495, 495, 60, 35],
	[1365, 495, 60, 35],
	[1365, 550, 60, 35],

	[1075, 194, 50, 20],
	[1075, 271, 50, 20],
	[1075, 349, 50, 20],
	[1075, 426, 50, 20],
	[1075, 504, 50, 20],
	[1075, 581, 50, 20],
	[1075, 659, 50, 20],
	[1075, 736, 50, 20],

	[1170, 194, 50, 20],
	[1170, 271, 50, 20],
	[1170, 349, 50, 20],
	[1170, 426, 50, 20],
	[1170, 504, 50, 20],
	[1170, 581, 50, 20],
	[1170, 659, 50, 20],
	[1170, 736, 50, 20],
]
const rois = {
  '3360_1440': [
    [1060, 667, 80, 36],

    [2220, 667, 80, 36],
    [2220, 740, 80, 36],

    [1830, 258, 75, 26],
    [1830, 362, 75, 26],
    [1830, 464, 75, 26],
    [1830, 568, 75, 26],
    [1830, 672, 75, 26],
    [1830, 775, 75, 26],
    [1830, 878, 75, 26],
    [1830, 980, 75, 26],

    [1960, 258, 75, 26],
    [1960, 362, 75, 26],
    [1960, 464, 75, 26],
    [1960, 568, 75, 26],
    [1960, 672, 75, 26],
    [1960, 775, 75, 26],
    [1960, 878, 75, 26],
    [1960, 980, 75, 26]
  ]
  // '1920_1080': [
  //   [493, 500, 62, 28],

  //   [1365, 500, 62, 28],
  //   [1365, 555, 60, 28],

  //   [1070, 193, 60, 24],
  //   [1070, 272, 60, 24],
  //   [1070, 346, 60, 24],
  //   [1070, 424, 60, 24],
  //   [1070, 504, 60, 24],
  //   [1070, 582, 60, 24],
  //   [1070, 658, 60, 24],
  //   [1070, 736, 60, 24],

  //   [1168, 193, 60, 24],
  //   [1168, 272, 60, 24],
  //   [1168, 346, 60, 24],
  //   [1168, 424, 60, 24],
  //   [1168, 504, 60, 24],
  //   [1168, 582, 60, 24],
  //   [1168, 658, 60, 24],
  //   [1168, 736, 60, 24]
  // ]
} as { [key: string]: number[][] }

@injectable
/** 开门服务 */
export default class DoorService {
  rois = ref(rois['3360_1440'])

  private worker!: Worker
  private imgFilePath = ''
  private canvas = document.createElement('canvas')

  private inputValue = 0
  private outputValue = {
    v1: 0,
    v2: 0
  }

  private calcValueL = [] as string[]
  private calcValueR = [] as string[]
  private resultValue = new Array(8).fill(false) as boolean[]

  constructor () {
    const isFs = location.protocol === 'file:'

    const width = window.screen.width
    const height = window.screen.height
    const rois = this.gen_rois(width, height)


    createWorker({
      workerPath: (isFs ? location.href.slice(0, location.href.slice(0, -3).lastIndexOf('/')) : location.origin) + '/worker.min.js',
      corePath: (isFs ? location.href.slice(0, location.href.slice(0, -3).lastIndexOf('/')) : location.origin) + '/tesseract-core.wasm.js',
      logger: m => console.log(m),
      langPath: (isFs ? location.href.slice(0, location.href.slice(0, -3).lastIndexOf('/')) : location.origin) + '/',
      gzip: false
    }).then(async w => {
      this.worker = w
      await this.worker.loadLanguage('eng')
      await this.worker.initialize('eng')
      await this.worker.setParameters({
        tessedit_char_whitelist: '0123456789+-*/'
      })

      this.imgFilePath = await join(await appCacheDir(), '__cap_for_ascum.png')
    });

    (async () => {
      if (!await exists(identifier, { dir: BaseDirectory.Document })) {
        await createDir(identifier, { dir: BaseDirectory.Document })
        // await writeTextFile(identifier + '\\3360_1440.screen.json', JSON.stringify([
        //   [1060, 667, 80, 36],

        //   [2220, 667, 80, 36],
        //   [2220, 740, 80, 36],

        //   [1830, 258, 75, 26],
        //   [1830, 362, 75, 26],
        //   [1830, 464, 75, 26],
        //   [1830, 568, 75, 26],
        //   [1830, 672, 75, 26],
        //   [1830, 775, 75, 26],
        //   [1830, 878, 75, 26],
        //   [1830, 980, 75, 26],

        //   [1960, 258, 75, 26],
        //   [1960, 362, 75, 26],
        //   [1960, 464, 75, 26],
        //   [1960, 568, 75, 26],
        //   [1960, 672, 75, 26],
        //   [1960, 775, 75, 26],
        //   [1960, 878, 75, 26],
        //   [1960, 980, 75, 26]
        // ]), { dir: BaseDirectory.Document })
        // await writeTextFile(identifier + '\\1920_1080.screen.json', JSON.stringify([
        //   [493, 498, 62, 30],
        //   [1365, 498, 62, 30],
        //   [1365, 553, 60, 30],

        //   [1070, 192, 60, 24],
        //   [1070, 270, 60, 24],
        //   [1070, 346, 60, 24],
        //   [1070, 424, 60, 24],
        //   [1070, 501, 60, 24],
        //   [1070, 580, 60, 24],
        //   [1070, 656, 60, 24],
        //   [1070, 734, 60, 24],

        //   [1168, 192, 60, 24],
        //   [1168, 270, 60, 24],
        //   [1168, 346, 60, 24],
        //   [1168, 424, 60, 24],
        //   [1168, 501, 60, 24],
        //   [1168, 580, 60, 24],
        //   [1168, 656, 60, 24],
        //   [1168, 734, 60, 24]
        // ]), { dir: BaseDirectory.Document })
        await createDir(identifier + '/imgs', { dir: BaseDirectory.Document, recursive: true })
      }
      // const scFiles = await readDir(identifier, { dir: BaseDirectory.Document })
      // for (const f of scFiles.filter(v => !v.children?.length && v.path.endsWith('.screen.json'))) {
      //   try {
      //     const sccfg = JSON.parse(await readTextFile(identifier + '/' + f.name, { dir: BaseDirectory.Document })) as number[][]
      //     if (Array.isArray(sccfg) && sccfg.length === 19 && sccfg.findIndex(v => v.length !== 4) < 0) {
      //       rois[f.name!.slice(0, -12)] = sccfg
      //     }
      //   } catch { }
      // }
      this.rois.value = rois // rois[`${window.screen.width}_${window.screen.height}`]
      // if (!this.rois.value) {
      //   this.rois.value = rois['3360_1440']
      // }
      console.log(this.rois.value)
    })()

    //
    this.canvas.style.cssText = 'position: fixed;top:0;left:0;z-index:100000;opacity:0;'
    document.body.appendChild(this.canvas)
  }

  async getPosition() {
    const rect: number[] = await invoke('get_position')
    if (rect) {
      return {
        x: rect[0],
        y: rect[1],
        width: rect[2],
        height: rect[3]
      }
    }
  }

  async detect () {
    const now = Date.now()
    const img = await this.capture(now)
    let results = [] as string[]
    let origin = cv.imread(img)
    let mat: Any
    const rect: number[] = await invoke('get_position')
    if (rect) {
      this.rois.value = this.gen_rois(rect[2], rect[3])
      mat = origin.roi(new cv.Rect(...rect))
    }
    const _rois = this.rois.value
    cv.cvtColor(mat, mat, cv.COLOR_BGR2GRAY)
    for (let i = 0; i < _rois.length; i++) {
      const _mat = mat.roi(new cv.Rect(..._rois[i]))
      if (_rois[i][3] < 80) {
        cv.resize(_mat, _mat, new cv.Size(Math.round(_rois[i][2] * 80 / _rois[i][3]), 80))
      }
      // 调整亮度
      for (let i = 0; i < _mat.rows; i++) {
        for (let j = 0; j < _mat.cols; j++) {
          _mat.ucharPtr(i, j)[0] = Math.max(0, Math.min(255, Math.round(_mat.ucharPtr(i, j)[0] * 1.7 - 105)))
        }
      }
      this.canvas.width = Math.round(_rois[i][2] * 80 / _rois[i][3])
      this.canvas.height = 80
      cv.imshow(this.canvas, _mat)
      const { data: { text } } = await this.worker.recognize(this.canvas.toDataURL())
      results[i] = text.trim()
      _mat.delete()
    }
    mat.delete()
    origin.delete()
    removeFile(identifier + '/imgs/capture_' + now + '.png', { dir: BaseDirectory.Document })
    results = results.map((v, i) => {
      if (i < 3) {
        return v
      }
      if (!v) {
      } else if (v.charCodeAt(0) > 47) {
        v = '*' + v
      } else if (v.length < 3 && !v.startsWith('/')) {
        v = '*' + v.slice(1)
      }
      return v
    })
    return results
  }

  openDoor (results: string[]) {
    this.inputValue = +results[0]
    this.outputValue = {
      v1: +results[1],
      v2: +results[2]
    }
    results.forEach((v, i) => {
      if (i < 3) {
        return
      }
      if (!v) {
        //
      } else if (v.charCodeAt(0) > 47) {
        v = '*' + v
      } else if (v.length < 3 && !v.startsWith('/')) {
        v = '*' + v.slice(1)
      }
      if (i > 10) {
        this.calcValueR[i - 11] = v
      } else {
        this.calcValueL[i - 3] = v
      }
    })
    this.resultValue = new Array(8).fill(false)
    for (let i = 0; i < 8; i++) {
      for (let j = 0; j < 8; j++) {
        if (this.check(j, i + 1, { v1: this.inputValue, v2: this.inputValue })) {
          return this.resultValue
        }
      }
    }
    return null
  }

  gatePosition(i: number, width = window.screen.width, height = window.screen.height) {
    const hscale = height / 1080
    const x = Math.round(width / 2 - (960 - 785) * hscale)
    const y = Math.round(231 * hscale + i * 77 * hscale)
    return [x / width, y / height]
  }

  // @ts-ignore
  // eslint-disable-next-line no-new-func, no-template-curly-in-string
  private execFunc = new Function('v', 'op', 'return eval(`${v}${op || ""}`)')

  private check (index: number, onN: number, input = { v1: 0, v2: 0 }): boolean {
    if (index + onN > 8) {
      return false
    }
    const _v1 = this.execFunc(input.v1, this.calcValueL[index])
    const _v2 = this.execFunc(input.v2, this.calcValueR[index])
    if (onN > 1) {
      if (this.check(index + 1, onN - 1, { v1: _v1, v2: _v2 })) {
        console.log(index, onN)
        this.resultValue[index] = true
        return true
      } else {
        return this.check(index + 1, onN, input)
      }
    } else {
      if (_v1 === this.outputValue.v1 && _v2 === this.outputValue.v2) {
        console.log(index, 1)
        this.resultValue[index] = true
        return true
      }
      return this.check(index + 1, onN, input)
    }
  }

  private async capture(now: number) {
    // 截图
    const file = await join(await documentDir(), identifier, 'imgs', 'capture_' + now + '.png')
    await invoke('capture', { file })
    // 加载图片
    const img = new Image()
    img.src = convertFileSrc(file) + '?v=' + Math.random()
    img.crossOrigin = 'anonymous'

    await new Promise(res => {
      let timer = setTimeout(() => res(null), 18000)
      img.onload = async () => {
        clearTimeout(timer)
        res(img)
      }
    })
    return img
  }

  private gen_rois(width: number, height: number) {
    const hscale = height / 1080
    const rois = [] as number[][]
    for (const [x, y, w, h] of ROIS_1080) {
      rois.push([
        Math.round(width / 2 - (960 - x) * hscale),
        Math.round(y * hscale),
        Math.round(w * hscale),
        Math.round(h * hscale),
      ])
    }
    return rois
  }
}
